package com.aigpt.common.module;

import cn.hutool.core.util.ClassUtil;
import com.aigpt.common.cons.CommonCons;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import java.util.Objects;
import java.util.Stack;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
@Component
public class GptAnswerFormatHandler {
    private static final String LEFT_OBJ_MARKS = "{(（";
    private static final String RIGHT_OBJ_MARKS = "})）";
    private static final String LEFT_ARR_MARKS = "[【";
    private static final String RIGHT_ARR_MARKS = "]】";
    private static final char CHAR_ONE_BLANK = ' ';
    private static final char CHAR_COMMA = ',';
    private static final char CHAR_QUOTATION_MARK = '"';
    private static final String DOUBLE_QUOTATION_MARK = "\"\"";
    private static final String LINE_END_MARK = "\",";
    private static final String LEFT_ARR_NEWLINE_MARKS = "[\n";
    private static final String RIGHT_ARR_NEWLINE_MARKS = "\n]";
    private static final String LEFT_OBJ_NEWLINE_MARKS = "{\n";
    private static final String RIGHT_OBJ_NEWLINE_MARKS = "\n}";
    private static final String REGEX = "[{\\[【（\"'‘“]?([a-zA-Z0-9_\\-\\u4e00-\\u9fa5/]+)[]}】）\"'’”]?\\s*[=:：]\\s*(.*)";
    private static final Pattern pattern = Pattern.compile(REGEX);

    /**
     * 复杂bean，例如多层嵌套Map或List：Map<String, Map<String, List<User>>>
     * 使用方式：gptContentFormatForJson(content, new TypeReference<Map<String, Map<String, List<User>>>>() {})
     * bean class 中的字段必须与应答内容中的字段名一致,否则需要定制key的转化
     *
     * @param content
     * @param type
     * @param <T>
     * @return
     */
    public <T> T gptContentFormatForJson(String content, TypeReference<T> type) {
        return gptContentFormatForJson(content, (Class<T>) type.getRawType(), type, null, null);
    }

    /**
     * 简单bean，如果无法格式化请使用TypeReference参数的函数
     * 通用GPT应答内容格式化，应用于json格式的应答数据
     * bean class 中的字段必须与应答内容中的字段名一致,否则需要定制key的转化
     *
     * @param content
     * @param beanCls
     * @param <T>
     * @return
     */
    public <T> T gptContentFormatForJson(String content, Class<T> beanCls) {
        return gptContentFormatForJson(content, beanCls, null, null, null);
    }

    /**
     * 简单bean，如果无法格式化请使用TypeReference参数的函数
     * 通用GPT应答内容格式化，应用于json格式的应答数据
     * bean class 中的字段必须与应答内容中的字段名一致,否则需要定制key的转化
     *
     * @param content
     * @param beanCls
     * @param kvHandle key、value定制处理
     * @param <T>
     * @return
     */
    public <T> T gptContentFormatKVForJson(String content, Class<T> beanCls, Function<ValidKVDTO, ValidKVDTO> kvHandle) {
        return gptContentFormatForJson(content, beanCls, null, kvHandle, null);
    }

    /**
     * 简单bean，如果无法格式化请使用TypeReference参数的函数
     * 通用GPT应答内容格式化，应用于json格式的应答数据
     * bean class 中的字段必须与应答内容中的字段名一致,否则需要定制key的转化
     *
     * @param content
     * @param beanCls
     * @param lineHandle 行内容处理函数拓展，组件会将内容转化为多行内容处理，每一行均会触发lineHandle函数（除class为基础类型外包括String）
     * @param <T>
     * @return
     */
    public <T> T gptContentFormatLineForJson(String content, Class<T> beanCls, Function<String, String> lineHandle) {
        return gptContentFormatForJson(content, beanCls, null, null, lineHandle);
    }

    /**
     * 通用GPT应答内容格式化，应用于json格式的应答数据
     * bean class 中的字段必须与应答内容中的字段名一致,否则需要定制key的转化
     *
     * @param content
     * @param beanCls
     * @param type
     * @param kvHandle   key、value处理函数拓展
     * @param lineHandle 行内容处理函数拓展，组件会将内容转化为多行内容处理，每一行均会触发lineHandle函数（除class为基础类型外包括String）
     * @param <T>
     * @return
     */
    private <T> T gptContentFormatForJson(String content, Class<T> beanCls, TypeReference<T> type, Function<ValidKVDTO, ValidKVDTO> kvHandle, Function<String, String> lineHandle) {
        long st = System.currentTimeMillis();
        boolean isSuccess = true;
        String formatBackJson = null;
        String tmpContent = content;
        try {
            if (StringUtils.isBlank(tmpContent) || Objects.isNull(beanCls)) {
                return null;
            }
            if (beanCls.equals(String.class)) {
                return (T) tmpContent;
            }
            if (ClassUtil.isSimpleValueType(beanCls)) {
                return JSON.parseObject(tmpContent, beanCls);
            }
            Stack<Boolean> stackMark = new Stack<>(); //true：对象，false：集合
            tmpContent = preHandle(tmpContent);
            String[] cs = tmpContent.split(CommonCons.NEW_LINE);
            StringBuilder contentBuffer = new StringBuilder();
            for (String c : cs) {
                if (StringUtils.isBlank(c)) {
                    continue;
                }
                c = c.trim();
                if (Objects.nonNull(lineHandle)) {
                    contentBuffer.append(lineHandle.apply(c));
                    continue;
                }
                if (objMarkHandle(c, stackMark, contentBuffer) || stackMark.isEmpty()) {
                    continue;
                }
                if (!stackMark.peek()) {
                    if (Objects.nonNull(kvHandle)) {
                        ValidKVDTO kvdto = kvHandle.apply(ValidKVDTO.builder().isElemOfColl(true).v(c).build());
                        if (Objects.nonNull(kvdto)) {
                            c = kvdto.v;
                        }
                    }
                    contentBuffer.append(fillLine(c));
                    contentBuffer.append(CommonCons.NEW_LINE);
                    continue;
                }
                Matcher matcher = pattern.matcher(c);
                boolean isFind = matcher.find();
                if (isFind && matcher.groupCount() >= 2) {
                    String key = matcher.group(1);
                    String val = matcher.group(2);
                    if (Objects.nonNull(kvHandle)) {
                        ValidKVDTO kvdto = kvHandle.apply(ValidKVDTO.builder().isElemOfColl(false).k(key).v(val).build());
                        if (Objects.nonNull(kvdto)) {
                            key = kvdto.k;
                            val = kvdto.v;
                        }
                    }
                    contentBuffer.append(CommonCons.CHAR_EN_SINGLE_QUOTATION_MARK);
                    contentBuffer.append(key);
                    contentBuffer.append(CommonCons.CHAR_EN_SINGLE_QUOTATION_MARK);
                    contentBuffer.append(CommonCons.COLON);
                    if (objMarkHandle(val, stackMark, contentBuffer)) {
                        continue;
                    }
                    contentBuffer.append(StringUtils.isBlank(val) ? CommonCons.BLANK : fillLine(val));
                }
            }
            while (!stackMark.isEmpty()) {
                contentBuffer.append(CommonCons.NEW_LINE);
                contentBuffer.append(stackMark.pop() ? CommonCons.RIGHT_BIG_BRACKET : CommonCons.RIGHT_MIDDLE_BRACKET);
            }
            if (contentBuffer.charAt(contentBuffer.length() - 1) == CommonCons.CHAR_COMMA) {
                formatBackJson = contentBuffer.substring(0, contentBuffer.length() - 1);
            } else {
                formatBackJson = contentBuffer.toString();
            }
            if (Objects.nonNull(type)) {
                return JSON.parseObject(formatBackJson, type);
            } else {
                return JSON.parseObject(formatBackJson, beanCls);
            }
        } catch (Exception e) {
            log.error("格式化失败\n格式化后:\n{}\n格式化前:\n{}", formatBackJson, content, e);
            isSuccess = false;
            try {
                if (Objects.nonNull(type)) {
                    return JSON.parseObject(content, type);
                } else {
                    return JSON.parseObject(content, beanCls);
                }
            } catch (Exception ex) {
                return null;
            }
        } finally {
            if (log.isDebugEnabled())
                log.debug("GPT应答JSON内容格式化,isSuccess:{},耗时:{}\n格式化后:\n{}\n格式化前:\n{}", isSuccess, System.currentTimeMillis() - st, formatBackJson, content);
        }
    }

    @Builder
    @Getter
    @Setter
    public static class ValidKVDTO {
        /**
         * 是否是集合的元素
         * true为集合中的元素，包括：Array、List、Set、stack、heap、vector (即json为[])
         * true的场景下，k 是 null
         */
        private boolean isElemOfColl;
        private String k;
        private String v;
    }

    private String preHandle(String content) {
        StringBuilder contentBuffer = new StringBuilder(content);
        int index = contentBuffer.length() - 1;
        int counter = 0;
        int addSteplen = 0;
        for (int i = 0; i <= index; i++) {
            int idx = i + addSteplen;
            char c = contentBuffer.charAt(idx);
            if (c == CommonCons.CHAR_EN_SINGLE_QUOTATION_MARK) {
                counter += counter > 0 ? -1 : 1;
                continue;
            }
            if (c == CommonCons.CHAR_LEFT_BIG_BRACKET && counter == 0) {
                addSteplen++;
                contentBuffer.replace(idx, idx + 1, LEFT_OBJ_NEWLINE_MARKS);
            }
            if (c == CommonCons.CHAR_RIGHT_BIG_BRACKET && counter == 0) {
                addSteplen++;
                contentBuffer.replace(idx, idx + 1, RIGHT_OBJ_NEWLINE_MARKS);
            }
            if (c == CommonCons.CHAR_LEFT_MIDDLE_BRACKET && counter == 0) {
                addSteplen++;
                contentBuffer.replace(idx, idx + 1, LEFT_ARR_NEWLINE_MARKS);
            }
            if (c == CommonCons.CHAR_RIGHT_MIDDLE_BRACKET && counter == 0) {
                addSteplen++;
                contentBuffer.replace(idx, idx + 1, RIGHT_ARR_NEWLINE_MARKS);
            }
        }
        return contentBuffer.toString();
    }

    private String fillLine(String line) {
        if (CommonCons.ONE_QUOTATION_MARK.equals(line) || CommonCons.COMMA.equals(line)) {
            return DOUBLE_QUOTATION_MARK;
        }
        int index = line.length() - 1;
        int validFirstIndex = 0;
        boolean isFirstQuotationMark = false;
        for (int i = 0; i <= index; i++) {
            char c;
            if (CHAR_ONE_BLANK == (c = line.charAt(i))) {
                continue;
            }
            if (validFirstIndex == 0) {
                validFirstIndex = i;
                isFirstQuotationMark = c == CHAR_QUOTATION_MARK;
                break;
            }
        }
        if (!isFirstQuotationMark) {
            line = CommonCons.ONE_QUOTATION_MARK + line.substring(validFirstIndex);
        }
        Integer validFirstCharIndex = null;
        int firstMark = 0;
        Integer validSecendCharIndex = null;
        boolean isQuotationMark = false;
        index = line.length() - 1;
        for (int i = index; i >= 0; i--) {
            char c;
            if (CHAR_ONE_BLANK == (c = line.charAt(i))) {
                continue;
            }
            if (Objects.isNull(validFirstCharIndex)) {
                validFirstCharIndex = index;
                firstMark = c == CHAR_COMMA ? 1 : c == CHAR_QUOTATION_MARK ? 2 : 0;
                continue;
            }
            if (Objects.isNull(validSecendCharIndex)) {
                validSecendCharIndex = index;
                isQuotationMark = c == CHAR_QUOTATION_MARK;
                continue;
            }
            if (Objects.nonNull(validSecendCharIndex)) {
                break;
            }
        }
        if (firstMark == 0) {
            return line + LINE_END_MARK;
        } else if (firstMark == 2) {
            return line + CommonCons.COMMA;
        } else {
            if (isQuotationMark) {
                return line;
            } else if (!isQuotationMark) {
                return line.substring(0, validSecendCharIndex + 1) + LINE_END_MARK;
            }
        }
        return line;
    }

    private boolean objMarkHandle(String c, Stack stackMark, StringBuilder contentBuffer) {
        if (LEFT_OBJ_MARKS.contains(c)) {
            contentBuffer.append(CommonCons.LEFT_BIG_BRACKET);
            contentBuffer.append(CommonCons.NEW_LINE);
            stackMark.push(true);
            return true;
        } else if (LEFT_ARR_MARKS.contains(c)) {
            contentBuffer.append(CommonCons.LEFT_MIDDLE_BRACKET);
            contentBuffer.append(CommonCons.NEW_LINE);
            stackMark.push(false);
            return true;
        }
        String first = String.valueOf(c.charAt(0));
        if (RIGHT_OBJ_MARKS.contains(first) || RIGHT_OBJ_MARKS.contains(c)) {
            contentBuffer.append(CommonCons.NEW_LINE);
            contentBuffer.append(CommonCons.RIGHT_BIG_BRACKET);
            contentBuffer.append(CommonCons.COMMA);
            stackMark.pop();
            return true;
        } else if (RIGHT_ARR_MARKS.contains(first) || RIGHT_ARR_MARKS.contains(c)) {
            contentBuffer.append(CommonCons.NEW_LINE);
            contentBuffer.append(CommonCons.RIGHT_MIDDLE_BRACKET);
            contentBuffer.append(CommonCons.COMMA);
            stackMark.pop();
            return true;
        }
        return false;
    }

    /**
     * 非json内容，简单格式化
     * 单层非标准json字符串格式化,不允许有数组、集合、bean
     * 类的字段名和解析出来的字段名需要一致，不一致请定制字段转化函数,Function<String, String> fieldHandleFunc
     *
     * @param content
     * @param cls
     * @param <T>
     * @return
     */
    public <T> T gptContentSimpleFormat(String content, Class<T> cls) {
        return gptContentSimpleFormat(content, cls, null);
    }

    public <T> T gptContentSimpleFormat(String content, Class<T> beanCls, Function<String, String> fieldHandleFunc) {
        long st = System.currentTimeMillis();
        boolean isSuccess = true;
        try {
            if (StringUtils.isBlank(content) || Objects.isNull(beanCls)) {
                return null;
            }
            if (beanCls.equals(String.class)) {
                return (T) content;
            }
            if (ClassUtil.isSimpleValueType(beanCls)) {
                return JSON.parseObject(content, beanCls);
            }
            content = content.replace(CommonCons.LEFT_BIG_BRACKET, CommonCons.BLANK).replace(CommonCons.RIGHT_BIG_BRACKET, CommonCons.BLANK)
                    .replace(CommonCons.LEFT_MIDDLE_BRACKET, CommonCons.BLANK).replace(CommonCons.RIGHT_MIDDLE_BRACKET, CommonCons.BLANK);
            StringBuilder contentBuffer = new StringBuilder(CommonCons.LEFT_BIG_BRACKET);
            String[] subCon = StringUtils.split(content, CommonCons.NEW_LINE);
            for (int i = 0; i < subCon.length; i++) {
                String n = subCon[i];
                Matcher matcher = pattern.matcher(n);
                if (matcher.find() && matcher.groupCount() >= 2) {
                    String key = matcher.group(1);
                    String val = matcher.group(2);
                    if (StringUtils.isBlank(key)) {
                        continue;
                    }
                    if (Objects.nonNull(fieldHandleFunc)) {
                        key = fieldHandleFunc.apply(key);
                    }
                    contentBuffer.append(CommonCons.CHAR_EN_SINGLE_QUOTATION_MARK);
                    contentBuffer.append(key);
                    contentBuffer.append(CommonCons.CHAR_EN_SINGLE_QUOTATION_MARK);
                    contentBuffer.append(CommonCons.COLON);
                    contentBuffer.append(fillLine(val));
                }
            }
            contentBuffer.append(CommonCons.RIGHT_BIG_BRACKET);
            return JSON.parseObject(contentBuffer.toString(), beanCls);
        } catch (Exception e) {
            log.error("格式化失败,内容:{}", content, e);
            isSuccess = false;
            return null;
        } finally {
            if (log.isDebugEnabled())
                log.debug("GPT应答JSON内容格式化,isSuccess:{},耗时:{},格式化内容:{}", isSuccess, System.currentTimeMillis() - st, content);
        }
    }
}
